﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace HL7lite
{
    public static class MessageHelper
    {
        private static string[] lineSeparators = { "\r\n", "\n\r", "\r", "\n" };

        public static List<string> SplitString(this string strStringToSplit, string splitBy, StringSplitOptions splitOptions = StringSplitOptions.None)
        {
            return strStringToSplit.Split(new string[] { splitBy }, splitOptions).ToList();
        }

        public static List<string> SplitString(this string strStringToSplit, char chSplitBy, StringSplitOptions splitOptions = StringSplitOptions.None)
        {
            return strStringToSplit.Split(new char[] { chSplitBy }, splitOptions).ToList();
        }

        public static List<string> SplitString(this string strStringToSplit, char[] chSplitBy, StringSplitOptions splitOptions = StringSplitOptions.None)
        {
            return strStringToSplit.Split(chSplitBy, splitOptions).ToList();
        }

        public static List<string> SplitMessage(string message)
        {
            return message.Split(lineSeparators, StringSplitOptions.None).Where(m => !string.IsNullOrWhiteSpace(m)).ToList();
        }

        public static string LongDateWithFractionOfSecond(DateTime dt)
        {
            return dt.ToString("yyyyMMddHHmmss.FFFF");
        }

        public static string[] ExtractMessages(string messages)
        {
            var expr = "\x0B(.*?)\x1C\x0D";
            var matches = Regex.Matches(messages, expr, RegexOptions.Singleline);
            var list = new List<string>();

            foreach (Match m in matches)
                list.Add(m.Groups[1].Value);

            return list.ToArray();
        }

        public static DateTime? ParseDateTime(this string dateTimeString, bool throwExeption = false)
        {
            return ParseDateTime(dateTimeString, out TimeSpan offset, throwExeption);
        }

        public static DateTime? ParseDateTime(this string dateTimeString, out TimeSpan offset, bool throwExeption = false)
        {
            var expr = @"^\s*((?:19|20)[0-9]{2})(?:(1[0-2]|0[1-9])(?:(3[0-1]|[1-2][0-9]|0[1-9])(?:([0-1][0-9]|2[0-3])(?:([0-5][0-9])(?:([0-5][0-9](?:\.[0-9]{1,4})?)?)?)?)?)?)?(?:([+-][0-1][0-9]|[+-]2[0-3])([0-5][0-9]))?\s*$";
            var matches = Regex.Matches(dateTimeString, expr, RegexOptions.Singleline);
            offset = TimeSpan.Zero;
            try
            {
                if (matches.Count != 1)
                    throw new FormatException("Invalid date format");

                var groups = matches[0].Groups;
                int year = int.Parse(groups[1].Value);
                int month = groups[2].Success ? int.Parse(groups[2].Value, CultureInfo.InvariantCulture) : 1;
                int day = groups[3].Success ? int.Parse(groups[3].Value, CultureInfo.InvariantCulture) : 1;
                int hours = groups[4].Success ? int.Parse(groups[4].Value, CultureInfo.InvariantCulture) : 0;
                int mins = groups[5].Success ? int.Parse(groups[5].Value, CultureInfo.InvariantCulture) : 0;

                float fsecs = groups[6].Success ? float.Parse(groups[6].Value, CultureInfo.InvariantCulture) : 0;
                int secs = (int)Math.Truncate(fsecs);
                int msecs = (int)Math.Truncate(fsecs * 1000) % 1000;

                int tzh = groups[7].Success ? int.Parse(groups[7].Value, CultureInfo.InvariantCulture) : 0;
                int tzm = groups[8].Success ? int.Parse(groups[8].Value, CultureInfo.InvariantCulture) : 0;
                offset = new TimeSpan(tzh, tzm, 0);

                return new DateTime(year, month, day, hours, mins, secs, msecs);
            }
            catch
            {
                if (throwExeption)
                    throw;

                return null;
            }
        }

        /// <summary>
        /// Serialize string to MLLP escaped byte array
        /// </summary>
        /// <param name="message">String to serialize</param>
        /// <param name="encoding">Text encoder (optional)</param>
        /// <returns>MLLP escaped byte array</returns>
        public static byte[] GetMLLP(string message, Encoding encoding = null)
        {
            if (encoding == null)
                encoding = Encoding.UTF8;
            
            byte[] data = encoding.GetBytes(message);
            byte[] buffer = new byte[data.Length + 3];
            buffer[0] = 11; // VT

            Array.Copy(data, 0, buffer, 1, data.Length);
            buffer[buffer.Length - 2] = 28; // FS
            buffer[buffer.Length - 1] = 13; // CR

            return buffer;
        }
        //====================================================================================================
        /// <summary>
        /// convert a HL7v2 formatted string to JSON
        /// </summary>
        /// <param name="hl7v2message"></param>
        /// <returns></returns>
        public static string ConvertToJson(this string hl7v2message)
        {
            return Hl7v2ToJson.Convert(hl7v2message);
        }
        /// <summary>
        /// convert a HL7v2 formatted string to dynamic
        /// </summary>
        /// <param name="hl7v2message"></param>
        /// <returns></returns>
        public static dynamic ConvertToDynamic(this string hl7v2message)
        {
            var json = Hl7v2ToJson.Convert(hl7v2message);
            if (string.IsNullOrWhiteSpace(json)) return new { };

            var jsobj = JsonConvert.DeserializeObject(json);
            if (jsobj == null) return new { };

            return ToDynamic(jsobj);
        }
        //====================================================================================================
        /// <summary>
        /// object 对象转dynamic
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static dynamic ToDynamic(this object obj)
        {
            if (obj == null)
                return new { };
            if (obj is System.Dynamic.ExpandoObject)
                return obj;

            var expando = new MyDynamicObject();
           // IDictionary<string, object> dy = expando;
            //---------------------------------------------------------------------------------------------------------
            if (obj is Newtonsoft.Json.Linq.JObject _jObject)//JSON Object
            {
                return ToDynamic(_jObject);
            }
            else if (obj is Newtonsoft.Json.Linq.JArray _ajObject)//JSON JArray
            {
                return ToDynamic(_ajObject);
            }
            else if (obj is System.Collections.IDictionary _dicObject)//IDictionary
            {
                foreach (object key in _dicObject.Keys)
                {
                    var val = _dicObject[key];
                    var dyPropName = key.ToString();
                    expando[dyPropName] = val;
                }
            }
            else//实体
            {
                return obj;
            }

            return expando;
        }
        /// <summary>
        /// Newtonsoft.Json.Linq.JObject对象转dynamic
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static dynamic ToDynamic(this Newtonsoft.Json.Linq.JObject obj)
        {
            if (obj == null)
                return new { };

            var expando = new MyDynamicObject();
            //IDictionary<string, object> dy = expando;
            foreach (var item in obj)
            {
                var val = item.Value;
                if (val != null)
                {
                    if (val is Newtonsoft.Json.Linq.JObject _sjObject)
                    {
                        expando[item.Key] = ToDynamic(_sjObject);
                    }
                    else if (val is Newtonsoft.Json.Linq.JArray _ajObject)
                    {
                        expando[item.Key] = ToDynamic(_ajObject);
                    }
                    else
                    {
                        expando[item.Key] = (val as Newtonsoft.Json.Linq.JValue).Value;
                    }
                    continue;
                }
                expando[item.Key] = null;
            }
            return expando;
        }
        /// <summary>
        /// Newtonsoft.Json.Linq.JArray 对象转dynamic
        /// </summary>
        /// <param name="jarray"></param>
        /// <returns></returns>
        public static dynamic ToDynamic(this Newtonsoft.Json.Linq.JArray jarray)
        {
            if (jarray == null)
                return new List<dynamic>();

            var expando = new List<dynamic>();
            foreach (var item in jarray)
            {
                if (item.HasValues)
                {
                    var val = item.Value<object>();
                    if (val != null)
                    {
                        if (val is Newtonsoft.Json.Linq.JObject _sjObject)
                        {
                            expando.Add(ToDynamic(_sjObject));
                        }
                        else if (val is Newtonsoft.Json.Linq.JArray _ajObject)
                        {
                            expando.Add(ToDynamic(_ajObject));
                        }
                        else
                        {
                            expando.Add(val);
                        }
                        continue;
                    }
                }
            }
            return expando;
        }
    }
    public class MyDynamicObject : DynamicObject
    {
        // The inner dictionary.
        private Dictionary<string, object> _Values = new Dictionary<string, object>();

        // Getting a property.
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            return _Values.TryGetValue(binder.Name, out result);
        }

        // Setting a property.
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            _Values[binder.Name] = value;
            return true;
        }
        public object N(int fieldName)
        {
            return N(fieldName.ToString());
        }
        public object N(string fieldName)
        {
            if (_Values.TryGetValue(fieldName, out object result))
                return result;
            return null;
        }
        public string ToJson()
        {
            return JsonConvert.SerializeObject(_Values);
        }
        /// <summary>
        /// this
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public object this[string name]
        {
            get { return _Values[name]; }
            set { _Values[name] = value; }
        }

        //public Dictionary<string, object> Values
        //{
        //    get { return this._Values; }
        //}
    }

}
